using RingBuffer;
using System;
using System.Buffers;
using System.Threading;

namespace kratos {
public interface IRecycle { void OnReset(); }

public delegate void RecycleFunc(Object o);

public static class ObjectPool<T> where T : class, IRecycle, new () {
  static SpinLock lock_ = new SpinLock();
  static GrowingRingBuffer<T> rb_ = new GrowingRingBuffer<T>();

  public static T Get() {
    bool lock_taken = false;
    try {
      lock_.Enter(ref lock_taken);
      if (rb_.Count > 0) {
        return rb_.Get();
      } else {
        return new T();
      }
    } finally {
      if (lock_taken) {
        lock_.Exit();
      }
    }
  }

  public static void Recycle(Object o) {
    if (o == null) {
      return;
    }
    T obj = (T) o;
    obj.OnReset();
    bool lock_taken = false;
    try {
      lock_.Enter(ref lock_taken);
      rb_.Put(obj);
    } finally {
      if (lock_taken) {
        lock_.Exit();
      }
    }
  }

  public static int Count {
    get {
      bool lock_taken = false;
      try {
        lock_.Enter(ref lock_taken);
        return rb_.Count;
      } finally {
        if (lock_taken) {
          lock_.Exit();
        }
      }
    }
  }
}

public static class ObjectPoolPlain<T> where T : class, new () {
  static SpinLock lock_ = new SpinLock();
  static GrowingRingBuffer<T> rb_ = new GrowingRingBuffer<T>();

  public static T Get() {
    bool lock_taken = false;
    try {
      lock_.Enter(ref lock_taken);
      if (rb_.Count > 0) {
        return rb_.Get();
      } else {
        return new T();
      }
    } finally {
      if (lock_taken) {
        lock_.Exit();
      }
    }
  }

  public static void Recycle(Object o) {
    if (o == null) {
      return;
    }
    T obj = (T) o;
    bool lock_taken = false;
    try {
      lock_.Enter(ref lock_taken);
      rb_.Put(obj);
    } finally {
      if (lock_taken) {
        lock_.Exit();
      }
    }
  }

  public static int Count {
    get {
      bool lock_taken = false;
      try {
        lock_.Enter(ref lock_taken);
        return rb_.Count;
      } finally {
        if (lock_taken) {
          lock_.Exit();
        }
      }
    }
  }
}

public static class GlobalBytePool {
  static ArrayPool<byte> pool_ = ArrayPool<byte>.Create();
  public static ArrayPool<byte> Pool => pool_;
}
}
